#include "WebKitCaller.h"

#include <sys/types.h>
#include <linux/unistd.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <boost/thread/thread.hpp>

namespace SpiderWebKitClient {

GlobalConfig* GlobalConfig::m_instance = NULL;

GlobalConfig::GlobalConfig()
{
    m_zk = new ZooKit();
    m_gZMQCTX = zmq_ctx_new();
}

GlobalConfig::~GlobalConfig()
{
    delete m_zk;
}

void* GlobalConfig::gZMQCTX()
{
    return m_gZMQCTX;
}

GlobalConfig* GlobalConfig::instance()
{
    if (!m_instance) {
        m_instance = new GlobalConfig();
    }
    return m_instance;
}

void GlobalConfig::connect(std::string zkhost)
{
    m_zk->connect(zkhost);
}

std::string GlobalConfig::getProtocolFromGroup(std::string group, bool isStatic)
{
    boost::unique_lock<boost::mutex> lock(m_mutex);
    std::list<std::string> childs;
    std::string protocol;
    if(m_zk->getChild(group, childs, 100)) {
        double score = -1.0;
        bool first = true;
        double tcurrpc = 0.0;
        double toverflowpc = 0.0;
        double ntoverflowpc = 0.0;
        std::list<ProcessRouter::ManagerInfoStruct> infos;
        std::list<std::string>::iterator it;
        for (it = childs.begin(); it != childs.end(); it ++) {
            ProcessRouter::ManagerInfoStruct is;
            if(m_zk->get(group, (*it), is, 10)) {
                int cs = isStatic ? is.scoresize() : is.dcoresize();
                if (cs == 0 || is.mempercent() < 26)
                    continue;

                tcurrpc += isStatic ? is.scurrentpc() :  is.dcurrentpc();
                int of = isStatic ? is.soverflowpc() : is.doverflowpc();
                if (of > 0) {
                    toverflowpc += of;
                } else if (of < 0) {
                    ntoverflowpc += of;
                }
                infos.push_back(is);
            }
        }

        ntoverflowpc = -ntoverflowpc;
        std::list<ProcessRouter::ManagerInfoStruct>::iterator iit;
        for (iit = infos.begin(); iit != infos.end(); iit ++) {
            double tcurrpcscore = 0.0;
            if (tcurrpc != 0) 
                tcurrpcscore = isStatic ? iit->scurrentpc()/tcurrpc : iit->dcurrentpc()/tcurrpc;

            double tsc = 0.2*(iit->mempercent()/100) - 0.6 * tcurrpcscore;
            int of = isStatic ? iit->soverflowpc() : iit->doverflowpc();
            if (of > 0)
                tsc -= 0.2*(of/toverflowpc);
            else
                tsc -= 0.2*(of/ntoverflowpc);

            //std::cout << iit->protocol() << " | " << tsc << " | " << tcurrpc << std::endl;
            if (first) {
                first = false;
                score = tsc;
                protocol = iit->protocol();
            } else if (tsc > score) {
                score = tsc;
                protocol = iit->protocol();
            }   
        }   
    }
    //std::cout << protocol << std::endl;
    return protocol;
}

WebKitCaller::WebKitCaller()
    : m_zmqClient(NULL)
    , m_failed(false)
    , m_connectFailed(false)
    , m_wkpid(-1)
{
}

void WebKitCaller::initDefault(std::string group, bool isStatic)
{
    std::string pid = boost::lexical_cast<std::string>(getpid());
    std::string tid = boost::lexical_cast<std::string>(boost::this_thread::get_id());
    m_clientID = getIPAddress() + ":";
    m_clientID += pid + "/" + tid;

    std::string protocol = GlobalConfig::instance()->getProtocolFromGroup(group, isStatic);

    if (protocol == "") {
        std::cerr << "NOTE: --- Get webkit core by http error ---" << std::endl;
        m_failed = true;
        return ;
    }

    m_zmqClient = zmq_socket(GlobalConfig::instance()->gZMQCTX(), ZMQ_REQ);
    if (m_zmqClient == NULL) {
        perror("Create socket error");
        m_failed = true;
        return ;
    }
    int ret = zmq_connect(m_zmqClient, protocol.c_str());
    if (ret == -1) {
        m_connectFailed = true;
        m_failed = true;
        return;
    }

    int times = 10;
    while(m_wkpid == -1) {   
        getFreePid(isStatic);
        usleep(100000);
        times --;
        if (times <= 0) {
            m_failed = true;
            break;
        }
    }
}

void WebKitCaller::initWithServerProtocol(std::string protocol, bool isStatic)
{
    std::string pid = boost::lexical_cast<std::string>(getpid());
    std::string tid = boost::lexical_cast<std::string>(boost::this_thread::get_id());
    m_clientID = getIPAddress() + ":";
    m_clientID += pid + "/" + tid;

    if (protocol == "") {
        std::cerr << "NOTE: --- Get webkit core error ---" << std::endl;
        m_failed = true;
        return ;
    }

    m_zmqClient = zmq_socket(GlobalConfig::instance()->gZMQCTX(), ZMQ_REQ);
    if (m_zmqClient == NULL) {
        perror("Create socket error");
        m_failed = true;
        return ;
    }
    int ret = zmq_connect(m_zmqClient, protocol.c_str());
    if (ret == -1) {
        m_failed = true;
        return;
    }

    int times = 10;
    while(m_wkpid == -1) {   
        getFreePid(isStatic);
        usleep(100000);
        times --;
        if (times <= 0) {
            m_failed = true;
            break;
        }
    }
}

WebKitCaller::~WebKitCaller()
{
    if (!m_failed && m_wkpid != -1) {
        ProcessRouter::MessagePackage package;

        std::string type_name = ProcessRouter::CommandReleasePid::descriptor()->full_name();
        package.set_name(type_name);

        ProcessRouter::CommandReleasePid *command = new ProcessRouter::CommandReleasePid();

        command->set_clientid(m_clientID);
        command->set_freepid(m_wkpid);

        package.set_allocated_commandreleasepid(command);

        std::string pmess;
        package.SerializeToString(&pmess);

        /*   send   */
        zmq_msg_t request;

        zmq_msg_init_size(&request, pmess.size());
        memcpy(zmq_msg_data(&request), pmess.c_str(), pmess.size());

        //std::cout << "Send command : " << pmess << std::endl;

        int ret = zmq_sendmsg(m_zmqClient, &request, 0);
        if (ret == -1) {
            //perror("Send call message error:");
            zmq_msg_close(&request);
        }
        zmq_msg_close(&request);
        /* send end */


        /*   recv   */
        /*
        zmq_msg_t reply;
        zmq_msg_init(&reply);

        zmq_recvmsg(m_zmqClient, &reply, 0);
        zmq_msg_close(&reply);
        */
    }

    if (m_zmqClient && !m_connectFailed)
        zmq_close(m_zmqClient);
}

void WebKitCaller::getFreePid(bool isStatic)
{
    ProcessRouter::MessagePackage package;

    std::string type_name = ProcessRouter::CommandGetFreePid::descriptor()->full_name();
    package.set_name(type_name);

    ProcessRouter::CommandGetFreePid *command = new ProcessRouter::CommandGetFreePid();

    command->set_clientid(m_clientID);
    command->set_isstatic(isStatic);

    package.set_allocated_commandgetfreepid(command);

    std::string pmess;
    package.SerializeToString(&pmess);

    /*   send   */
    zmq_msg_t request;

    zmq_msg_init_size(&request, pmess.size());
    memcpy(zmq_msg_data(&request), pmess.c_str(), pmess.size());

    //std::cout << "Send command : " << pmess << std::endl;

    int ret = zmq_sendmsg(m_zmqClient, &request, 0);
    if (ret == -1) {
        //perror("Send call message error:");
        zmq_msg_close(&request);
    }
    zmq_msg_close(&request);
    /* send end */


    /*   recv   */
    zmq_msg_t reply;
    zmq_msg_init(&reply);

    zmq_recvmsg(m_zmqClient, &reply, 0);
    std::string reply_message((char*)zmq_msg_data(&reply), zmq_msg_size(&reply));
    zmq_msg_close(&reply);

    //printf("收到:%d|%s\n", reply_message.size(), reply_message.c_str());
    
    ProcessRouter::CommandGetFreePid rcommand;
    if (!rcommand.ParseFromString(reply_message))
        fprintf(stderr, "Failed to parse MessagePackage.\n");

    m_wkpid = rcommand.freepid();
}

std::string WebKitCaller::getIPAddress()
{
    int sock_fd;
    struct ifconf conf;
    struct ifreq *ifr;
    struct sockaddr_in *sin;
    char buff[128] = {0};
    int num, i;
    std::string ipAddress;

    sock_fd = socket(PF_INET, SOCK_DGRAM, 0);
    if(sock_fd < 0){
        perror("socket");
        return "";
    }
    conf.ifc_len = 128;
    conf.ifc_buf = buff;

    ioctl(sock_fd, SIOCGIFCONF, &conf);
    num = conf.ifc_len / sizeof(struct ifreq);
    ifr = conf.ifc_req;

    for(i = 0; i < num; i++){
        sin = (struct sockaddr_in *)(&ifr->ifr_addr);

        ioctl(sock_fd, SIOCGIFFLAGS, ifr);
        if(((ifr->ifr_flags & IFF_LOOPBACK) == 0) && (ifr->ifr_flags & IFF_UP)){
            ipAddress = inet_ntoa(sin->sin_addr);
            break;
        }
        ifr++;
    }

    close(sock_fd);

    return ipAddress;
}

std::string WebKitCaller::getCallJson(long pageid, long funcid, std::list<std::string> &args)
{
    Json::FastWriter writer;
    Json::Value root(Json::objectValue);
    std::string jsonstr;

    std::string pageIdStr = boost::lexical_cast<std::string>(pageid);

    root["clientid"] = Json::Value(m_clientID);
    root["pageid"] = Json::Value(pageIdStr);
    root["funcid"] = Json::Value((int)funcid);

    int count = 0;
    std::list<std::string>::iterator argit;
    for (argit = args.begin(); argit != args.end(); argit ++) {
        std::string argkey = "arg";
        std::string encodestr;
        argkey += boost::lexical_cast<std::string>(count);
        std::string argstring = (*argit);
        Base64Encode(argstring, &encodestr);
        root[argkey.c_str()] = Json::Value(encodestr);
        count ++;
    }

    jsonstr = writer.write(root);

    return jsonstr;
}

std::string WebKitCaller::webKitCall(long pageid, long funcid, std::list<std::string> &args, int timeout)
{
    if (m_failed)
        return "";
    ProcessRouter::MessagePackage package;

    std::string type_name = ProcessRouter::RPCRequest::descriptor()->full_name();
    package.set_name(type_name);

    ProcessRouter::RPCRequest *req = new ProcessRouter::RPCRequest();
    std::string message = getCallJson(pageid, funcid, args);

    req->set_clientid(m_clientID);
    req->set_aimpid(m_wkpid);
    req->set_args(message);

    package.set_allocated_request(req);

    std::string pmess;
    package.SerializeToString(&pmess);

    /*   send   */
    zmq_msg_t request;

    zmq_msg_init_size(&request, pmess.size());
    memcpy(zmq_msg_data(&request), pmess.c_str(), pmess.size());

    //std::cout << "发送 : " << pmess << std::endl;

    int ret = zmq_sendmsg(m_zmqClient, &request, 0);
    if (ret == -1) {
        //perror("Send call message error:");
        zmq_msg_close(&request);
        return "";
    }
    zmq_msg_close(&request);
    /* send end */


    /*   recv   */
    if (timeout == 0) {
        zmq_msg_t reply;
        zmq_msg_init(&reply);

        zmq_recvmsg(m_zmqClient, &reply, 0);
        std::string reply_message((char*)zmq_msg_data(&reply), zmq_msg_size(&reply));
        zmq_msg_close(&reply);

        //std::cout << "收到: " << reply_message.size() << " | " << reply_message.c_str() << std::endl;
        
        ProcessRouter::RPCAnswer answer;
        
        if (!answer.ParseFromString(reply_message))
            fprintf(stderr, "Failed to parse MessagePackage.\n");

        if (answer.exception()) {
            m_failed = true;
            return "";
        }

        return answer.retval();

    } else {

        zmq_pollitem_t items [] = { { m_zmqClient, 0, ZMQ_POLLIN, 0 } };

        int rc = zmq_poll (items, 1, timeout);
        if (rc == -1) {
            perror("Call message error:");
            return "";
        }

        if (items [0].revents & ZMQ_POLLIN) { // recv
            zmq_msg_t reply;
            zmq_msg_init(&reply);

            zmq_recvmsg(m_zmqClient, &reply, 0);
            std::string reply_message((char*)zmq_msg_data(&reply), zmq_msg_size(&reply));
            zmq_msg_close(&reply);

            //std::cout << "收到: " << reply_message.size() << " | " << reply_message.c_str() << std::endl;

            ProcessRouter::RPCAnswer answer;

            if (!answer.ParseFromString(reply_message))
                fprintf(stderr, "Failed to parse MessagePackage.\n");

            if (answer.exception()) {
                m_failed = true;
                return "";
            }

            return answer.retval();
        } else {
            perror("Call error:");
            m_failed = true;
        }
    }
    /* else { // Retry }*/
    /* recv end */

    return "";
}

bool WebKitCaller::Base64Encode(const std::string& input, std::string* output)
{ 
    typedef boost::archive::iterators::base64_from_binary<boost::archive::iterators::transform_width<std::string::const_iterator, 6, 8> > Base64EncodeIterator; 
    std::stringstream result; 
    copy(Base64EncodeIterator(input.begin()) , Base64EncodeIterator(input.end()), std::ostream_iterator<char>(result)); 
    size_t equal_count = (3 - input.length() % 3) % 3;  
    for (size_t i = 0; i < equal_count; i++) {
        result.put('='); 
    } 
    *output = result.str(); 
    return output->empty() == false; 
}

bool WebKitCaller::Base64Decode(const std::string& input, std::vector<char> &output)
{
    typedef boost::archive::iterators::transform_width<boost::archive::iterators::binary_from_base64<std::string::const_iterator>, 8, 6> Base64DecodeIterator; 
    try { 
        std::basic_string<char> result(Base64DecodeIterator(input.begin()) , Base64DecodeIterator(input.end())); 
        output.insert(output.begin(), result.begin(), result.end());
    } catch(...) {
        std::cout << "NOTE: --- Base64 Decode To Buffer Error ---" << std::endl;
        return false; 
    }
    return output.empty() == false;

}

bool WebKitCaller::Base64Decode(const std::string& input, std::string* output)
{ 
    typedef boost::archive::iterators::transform_width<boost::archive::iterators::binary_from_base64<std::string::const_iterator>, 8, 6> Base64DecodeIterator; 
    std::stringstream result; 
    try { 
        copy(Base64DecodeIterator(input.begin()) , Base64DecodeIterator(input.end()), std::ostream_iterator<char>(result)); 
    } catch(...) { 
        return false; 
    } 
    *output = result.str().c_str();
    return output->empty() == false;
}

std::string WebKitCaller::getReturnStringValue(std::string message)
{
    if (m_failed)
        return "";

    Json::Reader reader;
    Json::Value value;
    std::string retval;
    std::string retstr;

    if (reader.parse(message, value)) {
        if(!value["retval"].isNull()) {
            retval = value["retval"].asString();
            Base64Decode(retval, &retstr);
        }
    }

    return retstr;
}

std::list<long> WebKitCaller::getReturnLongArrayValue(std::string message)
{
    std::list<long> ret;
    if (m_failed)
        return ret;

    Json::Reader reader;
    Json::Value value, arrayValue;
    std::string retval;
    std::string retstr;

    if (reader.parse(message, value)) {
        if(!value["retval"].isNull()) {
            retval = value["retval"].asString();
            Base64Decode(retval, &retstr);
        }
    }

    if (reader.parse(retstr, arrayValue)) {
        if(!arrayValue["array"].isNull()) {
            Json::Value array = arrayValue["array"];
            for (int i = 0; i < array.size(); i ++) {
                std::string item = array[i].asString();
                long itref = boost::lexical_cast<long>(item);
                ret.push_back(itref);
            }
        }
    }

    return ret;
}

long WebKitCaller::getReturnLongValue(std::string message)
{
    if (m_failed)
        return 0;

    long ret = 0;
    Json::Reader reader;
    Json::Value value;
    std::string retstr;
    std::string retval;

    if (reader.parse(message, value)) {
        if(!value["retval"].isNull()) {
            retval = value["retval"].asString();
            Base64Decode(retval, &retstr);

            try {
                ret = boost::lexical_cast<long>(retstr);
            } catch(boost::exception& e) {
                std::cout << "String to long error:" << retstr.size() << "|" << retstr << std::endl;
            }
        }
    }

    return ret;
}

void WebKitCaller::getReturnRectValue(std::string message, int* x, int* y, int* w, int* h)
{
    *x = 0; *y = 0; *w = 0; *h = 0;

    if (m_failed)
        return ;

    Json::Reader reader;
    Json::Value value, valueRect;
    std::string retstr;
    std::string retval;

    if (reader.parse(message, value)) {
        if(!value["retval"].isNull()) {
            retval = value["retval"].asString();
            Base64Decode(retval, &retstr);
        }
    }

    if (!retstr.empty()) {
        if (reader.parse(retstr, valueRect)) {
            if(!valueRect["x"].isNull())
                *x = valueRect["x"].asInt();
            if(!valueRect["y"].isNull())
                *y = valueRect["y"].asInt();
            if(!valueRect["width"].isNull())
                *w = valueRect["width"].asInt();
            if(!valueRect["height"].isNull())
                *h = valueRect["height"].asInt();
        }
    }
}

bool WebKitCaller::getReturnBufferValue(std::string message, std::vector<char> &output)
{
    if (m_failed)
        return false;

    Json::Reader reader;
    Json::Value value;
    std::string retval;

    if (reader.parse(message, value)) {
        if(!value["retval"].isNull()) {
            retval = value["retval"].asString();
            return Base64Decode(retval, output);
        }
    }

    return false;
}

long WebKitCaller::getReturnPageValue(std::string message, long *page)
{
    if (m_failed) {
        page = NULL;
        return NOTAVALIABLE_LOAD_ERROR;
    }

    Json::Reader reader;
    Json::Value value;
    std::string retval;
    std::string retstr;
    Json::Value valuePage;

    if (reader.parse(message, value)) {
        if(!value["retval"].isNull()) {
            retval = value["retval"].asString();
            Base64Decode(retval, &retstr);
        }
    }

    if (!retstr.empty()) {
        if (reader.parse(retstr, valuePage)) {
            if(!valuePage["page"].isNull()) {
                // here string must cast to long value.
                try {
                    *page = boost::lexical_cast<long>(valuePage["page"].asString());
                } catch(boost::exception& e) {
                    std::cout << "String to page ref error:" << retstr.size() << "|" << retstr << std::endl;
                }
            }
            if(!valuePage["error"].isNull()) {
                // here sting must cast to long value.
                try {
                    return boost::lexical_cast<long>(valuePage["error"].asString());
                } catch(boost::exception& e) {
                    std::cout << "String to page ref error:" << retstr.size() << "|" << retstr << std::endl;
                }
                std::cout << "error: " << valuePage["error"].asString() << std::endl;
            }
        }
    }

    return -1;
}

std::string WebKitCaller::getReturnDownloadInfoValue(std::string message, std::map<std::string, std::string> &headers)
{
    if (m_failed) {
        return "";
    }

    Json::Reader reader;
    Json::Value value;
    std::string retval;
    std::string retstr;
    std::string link;
    Json::Value valueInfo;

    if (reader.parse(message, value)) {
        if(!value["retval"].isNull()) {
            retval = value["retval"].asString();
            Base64Decode(retval, &retstr);
        }
    }

    if (!retstr.empty()) {
        if (reader.parse(retstr, valueInfo)) {
            if(!valueInfo["link"].isNull()) 
                link = valueInfo["link"].asString();

            if(!valueInfo["headers"].isNull()) {
                Json::Value headersValue = valueInfo["headers"];
                for (int i = 0; i < headersValue.size(); i ++) {
                    Json::Value v = headersValue[i];
                    headers.insert(std::map<std::string, std::string>::value_type(v["name"].asString(), v["value"].asString()));
                }
            }
        }
    }

    return link;
}

std::list<Json::Value> WebKitCaller::getReturnJsonObjArrayValue(std::string message)
{
    std::list<Json::Value> resultValues;

    if (m_failed)
        return resultValues;

    Json::Value value(Json::arrayValue);
    Json::Reader reader;
    std::string retval;
    std::string retstr;


    if (reader.parse(message, value)) {
        if(!value["retval"].isNull()) {
            retval = value["retval"].asString();
            Base64Decode(retval, &retstr);
        }
    }

    if (!reader.parse(retstr, value))
        return resultValues;

    for (unsigned int i = 0; i < value.size(); i ++)
        resultValues.push_back(value[i]);

    return resultValues;
}

bool WebKitCaller::checkAvailable()
{
    long ret = 0;
    std::list<std::string> arglist;

    std::string retMessage = webKitCall(0, WEBKIT_LONG_CHECK_AVAILABLE, arglist, 50000); // 50s
    ret = getReturnLongValue(retMessage);

    return ret == 0 ? false : true;
}

bool WebKitCaller::failed()
{
    return m_failed;
}

} // namespace SpiderWebKitClient

